﻿//-------------------------------------------------------------------
//	Copyright (c) 2010-2014  Zhirnov Andrey
//	This file is part of the "UX Standard Lib" project.
//	See copyright notice in "UX_STL_Core.h".
//-------------------------------------------------------------------

#ifndef _UXS_PHYSICS_VALUE_H
#define _UXS_PHYSICS_VALUE_H

#pragma once

#include "PhysicsDimension.h"
#include "../CompileTime/TStaticFloat.h"

namespace UX_STL
{
namespace UXTypes
{

	namespace _types_hidden_
	{

		template <typename ValueType, typename Scale, typename Power>
		struct Dimensions_ConvertValue_ScalePower {
			static ValueType Get (const ValueType &value) {
				return value * Pow( Scale::ToFloat<ValueType>(), Power::ToFloat<ValueType>() );
			}
		};
		
		template <typename ValueType, typename Scale, typename Power>
		struct Dimensions_ConvertValue_ScaleStaticPower
		{
			STATIC_ASSERTE( Power::IsInteger::value );

			typedef typename Scale::Pow< Power::N >::type	scale_t;

			static ValueType Get (const ValueType &value) {
				return value * scale_t::ToFloat<ValueType>();
			}
		};

		template <typename ValueType, typename Scale>
		struct Dimensions_ConvertValue_Scale {
			static ValueType Get (const ValueType &value) {
				return value * Scale::ToFloat<ValueType>();
			}
		};
		
		template <typename ValueType>
		struct Dimensions_ConvertValue_Zero {
			static ValueType Get (const ValueType &value) {
				return ValueType(0);
			}
		};
		
		template <typename ValueType>
		struct Dimensions_ConvertValue_ValueOnly {
			static ValueType const & Get (const ValueType &value) {
				return value;
			}
		};

		template <typename ValueType, typename Scale, typename Power>
		struct Dimensions_ConvertValue
		{
			static const bool	ScaleIsZero	= Scale::IsZero::value;
			static const bool	ScaleIsOne	= Scale::FastEqual< typename CompileTime::StaticFloat32< 1 >::type >::value;

			static const bool	PowerIsZero	= Power::IsZero::value;
			static const bool	PowerIsOne	= Power::EqualInt< 1 >::value;

			static const int	Index		=	ScaleIsZero ? 0 :
												PowerIsZero ? 1 :
												ScaleIsOne  ? 2 :
												PowerIsOne  ? 3 :
												Power::IsInteger::value ? 4 :
												5;

			typedef typename CompileTime::TypeList10<
						Dimensions_ConvertValue_Zero< ValueType >,							// [0]  value = value * 0 ^ pow
						Dimensions_ConvertValue_ValueOnly< ValueType >,						// [1]  value = value * scale ^ 0
						Dimensions_ConvertValue_ValueOnly< ValueType >,						// [2]  value = value * 1 ^ pow
						Dimensions_ConvertValue_Scale< ValueType, Scale >,					// [3]  value = value * scale ^ 1
						Dimensions_ConvertValue_ScaleStaticPower< ValueType, Scale, Power >,// [4]  value = value * scale (^ pow)
						Dimensions_ConvertValue_ScalePower< ValueType, Scale, Power >		// [5]  value = value * scale ^ pow
					>::type											ConvesionsList_t;
						
			typedef typename ConvesionsList_t::Get< Index >::type	Conversion;
		};


		template <typename ValueType, typename Scale, typename Power>
		struct DimensionConversion_AddEq
		{
			typedef Scale	scale_t;
			typedef Power	power_t;
			
			static ValueType Get (const ValueType &left, const ValueType &right) {
				return left + right;
			}
		};

		template <typename ValueType, typename LeftScale, typename LeftPower, typename RightScale, typename RightPower>
		struct DimensionConversion_Add
		{
			typedef typename CompileTime::TStaticFloat< typename LeftScale::mantissa_t, 
								typename LeftScale::exponent_t, 1, 0 >::Simplify::type						scale_t;

			typedef typename CompileTime::TFractional< typename LeftPower::value_t, 1, 1 >::Simplified		power_t;
			
			static ValueType Get (const ValueType &left, const ValueType &right)
			{
				typedef typename Dimensions_ConvertValue< ValueType, LeftScale, LeftPower >::Conversion		LFunc;
				typedef typename Dimensions_ConvertValue< ValueType, RightScale, RightPower >::Conversion	RFunc;

				return LFunc::Get( left ) + RFunc::Get( right );
			}
		};


		template <typename ValueType, typename Scale, typename Power>
		struct DimensionConversion_SubEq
		{
			typedef Scale	scale_t;
			typedef Power	power_t;
			
			static ValueType Get (const ValueType &left, const ValueType &right) {
				return left - right;
			}
		};

		template <typename ValueType, typename LeftScale, typename LeftPower, typename RightScale, typename RightPower>
		struct DimensionConversion_Sub
		{
			typedef typename CompileTime::TStaticFloat< typename LeftScale::mantissa_t, 
								typename LeftScale::exponent_t, 1, 0 >::Simplify::type						scale_t;

			typedef typename CompileTime::TFractional< typename LeftPower::value_t, 1, 1 >::Simplified		power_t;
			
			static ValueType Get (const ValueType &left, const ValueType &right)
			{
				typedef typename Dimensions_ConvertValue< ValueType, LeftScale, LeftPower >::Conversion		LFunc;
				typedef typename Dimensions_ConvertValue< ValueType, RightScale, RightPower >::Conversion	RFunc;

				return LFunc::Get( left ) - RFunc::Get( right );
			}
		};
		

		template <typename ValueType, typename LeftScale, typename LeftPower, typename RightScale, typename RightPower>
		struct DimensionConversion_MulEqScale
		{
			STATIC_ASSERTE( LeftScale::Equal< RightScale >::value );

			typedef LeftScale										scale_t;
			typedef typename LeftPower::Add< RightPower >::type		power_t;

			static ValueType Get (const ValueType &left, const ValueType &right) {
				return left * right;
			}
		};

		template <typename ValueType, typename LeftScale, typename LeftPower, typename RightScale, typename RightPower>
		struct DimensionConversion_MulIntPow
		{
			STATIC_ASSERTE( LeftPower::IsInteger::value and RightPower::IsInteger::value );

			typedef typename LeftScale::Pow< LeftPower::Round::value >::type	lscale_t;
			typedef typename RightScale::Pow< RightPower::Round::value >::type	rscale_t;

			typedef typename lscale_t::Mul< rscale_t >::type											scale_t;
			typedef typename CompileTime::TFractional< typename LeftPower::value_t, 1, 1 >::Simplified	power_t;
			
			static ValueType Get (const ValueType &left, const ValueType &right) {
				return left * right;
			}
		};

		template <typename ValueType, typename LeftScale, typename LeftPower, typename RightScale, typename RightPower>
		struct DimensionConversion_Mul
		{
			typedef typename CompileTime::TStaticFloat< typename LeftScale::mantissa_t, 
								typename LeftScale::exponent_t, 1, 0 >::Simplify::type						scale_t;

			typedef typename CompileTime::TFractional< typename LeftPower::value_t, 1, 1 >::Simplified		power_t;

			static ValueType Get (const ValueType &left, const ValueType &right)
			{
				typedef typename Dimensions_ConvertValue< ValueType, LeftScale, LeftPower >::Conversion		LFunc;
				typedef typename Dimensions_ConvertValue< ValueType, RightScale, RightPower >::Conversion	RFunc;

				return LFunc::Get( left ) * RFunc::Get( right );
			}
		};
		

		template <typename ValueType, typename LeftScale, typename LeftPower, typename RightScale, typename RightPower>
		struct DimensionConversion_DivEqScale
		{
			STATIC_ASSERTE( LeftScale::FastEqual< RightScale >::value );

			typedef LeftScale										scale_t;
			typedef typename LeftPower::Sub< RightPower >::type		power_t;

			static ValueType Get (const ValueType &left, const ValueType &right) {
				return left / right;
			}
		};

		template <typename ValueType, typename LeftScale, typename LeftPower, typename RightScale, typename RightPower>
		struct DimensionConversion_DivIntPow
		{
			STATIC_ASSERTE( LeftPower::IsInteger::value and RightPower::IsInteger::value );

			typedef typename LeftScale::Pow< LeftPower::Round::value >::type	lscale_t;
			typedef typename RightScale::Pow< RightPower::Round::value >::type	rscale_t;

			typedef typename lscale_t::Div< rscale_t >::type											scale_t;
			typedef typename CompileTime::TFractional< typename LeftPower::value_t, 1, 1 >::Simplified	power_t;
			
			static ValueType Get (const ValueType &left, const ValueType &right) {
				return left / right;
			}
		};

		template <typename ValueType, typename LeftScale, typename LeftPower, typename RightScale, typename RightPower>
		struct DimensionConversion_Div
		{
			typedef typename CompileTime::TStaticFloat< typename LeftScale::mantissa_t, 
								typename LeftScale::exponent_t, 1, 0 >::Simplify::type						scale_t;

			typedef typename CompileTime::TFractional< typename LeftPower::value_t, 1, 1 >::Simplified		power_t;

			static ValueType Get (const ValueType &left, const ValueType &right)
			{
				typedef typename Dimensions_ConvertValue< ValueType, LeftScale, LeftPower >::Conversion		LFunc;
				typedef typename Dimensions_ConvertValue< ValueType, RightScale, RightPower >::Conversion	RFunc;

				return LFunc::Get( left ) / RFunc::Get( right );
			}
		};
		

		template <typename Scale, typename Power>
		struct DimensionConversion_Simplify_Zero {
			typedef typename CompileTime::TStaticFloat< typename Scale::mantissa_t, typename Scale::exponent_t, 0, 0 >	scale_t;
			typedef typename CompileTime::TFractional< typename Power::value_t, 1, 1 >									power_t;
		};

		template <typename Scale, typename Power>
		struct DimensionConversion_Simplify_One {
			typedef typename CompileTime::TStaticFloat< typename Scale::mantissa_t, typename Scale::exponent_t, 1, 0 >	scale_t;
			typedef typename CompileTime::TFractional< typename Power::value_t, 1, 1 >									power_t;
		};

		template <typename Scale, typename Power>
		struct DimensionConversion_Simplify_None {
			typedef Scale	scale_t;
			typedef Power	power_t;
		};

		template <typename Scale, typename Power>
		struct DimensionConversion_Simplify
		{
			typedef typename CompileTime::TStaticFloat< typename Scale::mantissa_t, typename Scale::exponent_t, 1, 0 >	one_t;

			typedef typename SwitchType< (Scale::IsZero::value),
						DimensionConversion_Simplify_Zero< Scale, Power >,
						typename SwitchType< (Power::IsZero::value or Scale::FastEqual< one_t >::value),
							DimensionConversion_Simplify_One< Scale, Power >,
							DimensionConversion_Simplify_None< Scale, Power >
						>::type
					>::type			type;
		};

	}	// _types_hidden_



	//
	// Physics Value Conversions
	//
	
	template <typename ValueType>
	struct PhysicsDimensionScale
	{
		typedef typename TypeInfo< ValueType >::Similar_Int															mantissa_t;
		typedef typename NearInt::SignedSize< CompileTime::MinMax< int, (sizeof(ValueType)>>2), 1 >::max >::type	exponent_t;
		typedef mantissa_t																							fractional_int_t;

		template <mantissa_t Mantissa, exponent_t Exponent = 0>
		struct Float : CompileTime::TStaticFloat< mantissa_t, exponent_t, Mantissa, Exponent >
		{};

		template <fractional_int_t Numerator, fractional_int_t Denominator = 1>
		struct Fractional : CompileTime::TFractional< fractional_int_t, Numerator, Denominator >
		{};


		// value * scale ^ pow
		template <mantissa_t ScaleMantissa,
				  exponent_t ScaleExponent = 0,
				  fractional_int_t PowNumerator = 1,
				  fractional_int_t PowDenominator = 1
				 >
		struct BaseConversion
		{
			typedef typename Float< ScaleMantissa, ScaleExponent >::Simplify::type								Scale;
			typedef typename Fractional< PowNumerator, PowDenominator >::Simplified								Power;
			typedef typename _types_hidden_::Dimensions_ConvertValue< ValueType, Scale, Power >::Conversion		Func;
			typedef BaseConversion< ScaleMantissa, ScaleExponent, PowNumerator, PowDenominator >				Self;

			template <typename Conv>
			struct Equal {
				static const bool	value = Scale::Equal< Conv::Scale >::value and
											Power::Equal< Conv::Power >::value;
			};

			struct Simplify {
				typedef typename _types_hidden_::DimensionConversion_Simplify< Scale, Power >::type	_simp_t;
				typedef BaseConversion< _simp_t::scale_t::MANTISSA, _simp_t::scale_t::EXPONENT,
										_simp_t::power_t::N, _simp_t::power_t::D >					type;
			};

			template <typename Conv>
			struct Add {
				typedef typename Conv::Scale			rscale_t;
				typedef typename Conv::Power			rpower_t;

				typedef typename SwitchType< Equal< Conv >::value,
							_types_hidden_::DimensionConversion_AddEq< ValueType, Scale, Power >,
							_types_hidden_::DimensionConversion_Add< ValueType, Scale, Power, rscale_t, rpower_t >
						>::type		add_op;
					
				typedef typename BaseConversion< add_op::scale_t::MANTISSA, add_op::scale_t::EXPONENT,
									add_op::power_t::N, add_op::power_t::D >::Simplify::type				type;

				static ValueType Get (const ValueType &left, const ValueType &right) {
					return add_op::Get( left, right );
				}
			};

			template <typename Conv>
			struct Sub {
				typedef typename Conv::Scale			rscale_t;
				typedef typename Conv::Power			rpower_t;

				typedef typename SwitchType< Equal< Conv >::value,
							_types_hidden_::DimensionConversion_SubEq< ValueType, Scale, Power >,
							_types_hidden_::DimensionConversion_Sub< ValueType, Scale, Power, rscale_t, rpower_t >
						>::type		sub_op;
					
				typedef typename BaseConversion< sub_op::scale_t::MANTISSA, sub_op::scale_t::EXPONENT,
									sub_op::power_t::N, sub_op::power_t::D >::Simplify::type				type;

				static ValueType Get (const ValueType &left, const ValueType &right) {
					return sub_op::Get( left, right );
				}
			};

			template <typename Conv>
			struct Mul {
				typedef typename Conv::Scale			rscale_t;
				typedef typename Conv::Power			rpower_t;

				typedef typename SwitchType< (Scale::FastEqual< rscale_t >::value),
							_types_hidden_::DimensionConversion_MulEqScale< ValueType, Scale, Power, rscale_t, rpower_t >,
							typename SwitchType< (Power::IsInteger::value and Conv::Power::IsInteger::value),
								_types_hidden_::DimensionConversion_MulIntPow< ValueType, Scale, Power, rscale_t, rpower_t >,
								_types_hidden_::DimensionConversion_Mul< ValueType, Scale, Power, rscale_t, rpower_t >
							>::type
						>::type		mul_op;

				typedef typename BaseConversion< mul_op::scale_t::MANTISSA, mul_op::scale_t::EXPONENT,
									mul_op::power_t::N, mul_op::power_t::D >::Simplify::type				type;

				static ValueType Get (const ValueType &left, const ValueType &right) {
					return mul_op::Get( left, right );
				}
			};

			template <typename Conv>
			struct Div {
				typedef typename Conv::Scale			rscale_t;
				typedef typename Conv::Power			rpower_t;

				typedef typename SwitchType< (Scale::FastEqual< rscale_t >::value),
							_types_hidden_::DimensionConversion_DivEqScale< ValueType, Scale, Power, rscale_t, rpower_t >,
							typename SwitchType< (Power::IsInteger::value and Conv::Power::IsInteger::value),
								_types_hidden_::DimensionConversion_DivIntPow< ValueType, Scale, Power, rscale_t, rpower_t >,
								_types_hidden_::DimensionConversion_Div< ValueType, Scale, Power, rscale_t, rpower_t >
							>::type
						>::type		div_op;
					
				typedef typename BaseConversion< div_op::scale_t::MANTISSA, div_op::scale_t::EXPONENT,
									div_op::power_t::N, div_op::power_t::D >::Simplify::type				type;

				static ValueType Get (const ValueType &left, const ValueType &right) {
					return div_op::Get( left, right );
				}
			};

			template <typename Conv>
			struct UnitMul {
				STATIC_ASSERTE( Power::IsInteger::value and Conv::Power::IsInteger::value );
				typedef typename Mul< Conv >::type	type;
			};

			template <typename Conv>
			struct UnitDiv {
				STATIC_ASSERTE( Power::IsInteger::value and Conv::Power::IsInteger::value );
				typedef typename Div< Conv >::type	type;
			};

			template <typename FracPow>
			struct Pow {
				typedef typename Power::Mul< FracPow >::type								pow_t;
				typedef typename BaseConversion< Scale::MANTISSA, Scale::EXPONENT,
													pow_t::N, pow_t::D >::Simplify::type	type;
			};

			struct Inverse {
				typedef typename Pow< Fractional< -1 > >::type	type;
			};

			template <typename NewValueType>
			struct To {
				typedef PhysicsDimensionScale< NewValueType >	_dim_t;
				typedef typename _dim_t::mantissa_t				_mantissa_t;
				typedef typename _dim_t::exponent_t				_exponent_t;
				typedef typename _dim_t::fractional_int_t		_fract_int_t;

				typedef typename _dim_t::BaseConversion<
										_mantissa_t( Scale::MANTISSA ), _exponent_t( Scale::EXPONENT ),
										_fract_int_t( Power::N ), _fract_int_t( Power::D ) >				type;
			};


			static inline ValueType Get (const ValueType &value) {
				return Func::Get( value );
			}

			static inline ValueType ToFloat () {
				return UXMath::Pow( Scale::ToFloat< ValueType >(), Power::ToFloat< ValueType >() );
			}

			static inline string ToString () {
				return string() << '(' << Scale::ToString() << ")^" << Power::ToString();
			}
		};

#		define CONVERSION( _scale_m, _scale_e, _name ) \
			typedef typename BaseConversion< _scale_m, _scale_e, 1, 1 >::Simplify::type		_name

		typedef BaseConversion< 1, 0, 1, 1 >	None;

		CONVERSION( 1,		 1,			Deca	);		// da
		CONVERSION( 1,		 2,			Hecto	);		// h
		CONVERSION( 1,		 3,			Kilo	);		// k
		CONVERSION( 1,		 6,			Mega	);		// M
		CONVERSION( 1,		 9,			Giga	);		// G
		CONVERSION( 1,		12,			Tera	);		// T
		CONVERSION( 1,		15,			Peta	);		// P
		CONVERSION( 1,		18,			Exa		);		// E
		CONVERSION( 1,		21,			Zetta	);		// Z
		CONVERSION( 1,		24,			Yotta	);		// Y

		CONVERSION( 1,		 -1,		Deci	);		// d
		CONVERSION( 1,		 -2,		Centi	);		// c
		CONVERSION( 1,		 -3,		Milli	);		// m
		CONVERSION( 1,		 -6,		Micro	);		// u
		CONVERSION( 1,		 -9,		Nano	);		// n
		CONVERSION( 1,		-12,		Pico	);		// p
		CONVERSION( 1,		-15,		Femto	);		// f
		CONVERSION( 1,		-18,		Atto	);		// a
		CONVERSION( 1,		-21,		Zepto	);		// z
		CONVERSION( 1,		-24,		Yocto	);		// y

		CONVERSION( 6,			   1,	Minute	);		// s	min
		CONVERSION( 6*6,		   2,	Hour	);		// s	h
		CONVERSION( 6*6*24,		   2,	Day		);		// s	d
		CONVERSION( 6*6*24*36525,  0,	Year	);		// s	y

		CONVERSION( 1,			  -3,	Litre				);		// m^3	L
		CONVERSION( 378541178,	-3-8,	USGallon			);		// m^3	gal

		CONVERSION( 160217733,	-19-8,	ElectronVolt		);		// J	eV
		CONVERSION( 1660538922, -27-9,	Dalton				);		// kg	Da	amu
		CONVERSION( 1495978707,	    2,	AstronomicalUnit	);		// m	au

		CONVERSION( 1852,		   0,	NauticalMile		);		// m	
		CONVERSION( 1609344,	  -3,	StatuteMile			);		// m	
		CONVERSION(	254,		-2-2,	Inch				);		// m	in
		CONVERSION( 3048,		  -4,	Foot				);		// m	ft
		CONVERSION( 4828032,	  -3,	League				);		// m	
		CONVERSION( 9144,		  -4,	Yard				);		// m

		CONVERSION( 6479891,	-6-5,	Grain				);		// kg	gr
		CONVERSION(	45359237,	  -8,	Pound				);		// kg	lb

		CONVERSION( 1,			 -10,	Angstrom			);		// m	A
		CONVERSION( 1,			 -28,	Barn				);		// m^2	b

		CONVERSION( 1,			   5,	Bar					);		// Pa	bar
		CONVERSION( 1013,		 3-1,	Atmosphere			);		// Pa

		CONVERSION( 299792458,	   0,	SpeedOfLight		);		// m/s	c
		CONVERSION( 30856776,	16-7,	Parsec				);		// m	pc

		CONVERSION( 667384808,	-11-8,	GravitationalConstant		);	// m^3 / (s^2 * kg)
		CONVERSION( 980665,		   -5,	GravitationalAcceleration	);	// m / s^2

		CONVERSION( 602214078,  23-8,	AvogadroConstant			);	// 1/mol
		CONVERSION( 1256637061, -6-9,	VacuumPermeabilityConst		);	// kg * m / (s * A)^2
		CONVERSION( 83144621,	  -7,	GasConstant					);	// R = J / (K * mol)

		//CONVERSION( 6371,		   3,	EarthRadius		);
		CONVERSION( 59736,		24-4,	EarthMass		);
		CONVERSION( 198892,		30-5,	SolarMass		);
		CONVERSION( 3846,		26-3,	SolarLuminosity	);
		CONVERSION( 6955,		 8-3,	SolarRadius		);
		
		CONVERSION( 8,			   0,	Byte			);		// byte

		CONVERSION( 1024,		   0,	Kilobit			);		// bit	1 << 10
		CONVERSION( 1048576,	   0,	Megabit			);		// bit	1 << 20
		CONVERSION( 1073741824,	   0,	Gigabit			);		// bit	1 << 30
		CONVERSION( 1099511628,	   3,	Terabit			);		// bit	1 << 40
		CONVERSION( 1125899907,	   6,	Petabit			);		// bit	1 << 50
		CONVERSION( 1152921505,	   9,	Exabit			);		// bit	1 << 60
		CONVERSION( 1180591621,	  12,	Zettabit		);		// bit	1 << 70
		CONVERSION( 1208925820,	  15,	Yottabit		);		// bit	1 << 80

		CONVERSION( 795774715,	  -7,	Oersted			);		// A/m	Oe
		
#		undef CONVERSION
	};



	//
	// Physics Value
	//

	template <typename ValueType,
			  typename Dimensions,
			  typename ValueScale = typename PhysicsDimensionScale< ValueType >::None
			 >
	struct PhysicsValue
	{
	// types
	public:	
		typedef ValueType												value_t;
		typedef Dimensions												dimensions_t;
		typedef ValueScale												value_scale_t;
		typedef PhysicsValue< value_t, dimensions_t, value_scale_t >	Self;

	private:
		template <typename D, typename S>
		struct _Add {
			typedef PhysicsValue< value_t, D, S >									Right;
			typedef typename value_scale_t::Add< typename Right::value_scale_t >	conv_add_op_t;
			typedef PhysicsValue< value_t,
						typename dimensions_t::Add< Right::dimensions_t >::type,
						typename conv_add_op_t::type >								type;

			static value_t Get (const value_t &left, const value_t &right) {
				return conv_add_op_t::Get( left, right );
			}
		};
		
		template <typename D, typename S>
		struct _Sub {
			typedef PhysicsValue< value_t, D, S >									Right;
			typedef typename value_scale_t::Sub< typename Right::value_scale_t >	conv_sub_op_t;
			typedef PhysicsValue< value_t,
						typename dimensions_t::Sub< Right::dimensions_t >::type,
						typename conv_sub_op_t::type >								type;

			static value_t Get (const value_t &left, const value_t &right) {
				return conv_sub_op_t::Get( left, right );
			}
		};
		
		template <typename D, typename S>
		struct _Mul {
			typedef PhysicsValue< value_t, D, S >									Right;
			typedef typename value_scale_t::Mul< typename Right::value_scale_t >	conv_mul_op_t;
			typedef PhysicsValue< value_t,
						typename dimensions_t::Mul< Right::dimensions_t >::type,
						typename conv_mul_op_t::type >								type;

			static value_t Get (const value_t &left, const value_t &right) {
				return conv_mul_op_t::Get( left, right );
			}
		};
		
		template <typename D, typename S>
		struct _Div {
			typedef PhysicsValue< value_t, D, S >									Right;
			typedef typename value_scale_t::Div< typename Right::value_scale_t >	conv_div_op_t;
			typedef PhysicsValue< value_t,
						typename dimensions_t::Div< Right::dimensions_t >::type,
						typename conv_div_op_t::type >								type;

			static value_t Get (const value_t &left, const value_t &right) {
				return conv_div_op_t::Get( left, right );
			}
		};
		
		template <isize PowNum, isize PowDenom = 1>
		struct _Pow {
			typedef typename CompileTime::Fractional32< PowNum, PowDenom >::type	pow_t;
			typedef PhysicsValue< value_t,
						typename dimensions_t::Power< pow_t >::type,
						typename value_scale_t::Pow< pow_t >::type >				type;
		};

		template <typename NewValueType>
		struct _NewConv {
			typedef typename PhysicsDimensionScale< NewValueType >::
						BaseConversion< value_scale_t::Scale::MANTISSA, value_scale_t::Scale::EXPONENT,
										value_scale_t::Power::N, value_scale_t::Power::D >	type;
		};

		typedef PhysicsValue< ValueType, typename dimensions_t::Inverse::type,
							  typename value_scale_t::Inverse::type >				SelfInversed;


	// variables
	private:
		value_t	_value;


	// methods
	public:
		PhysicsValue () : _value(0) {}
	
		explicit
		PhysicsValue (value_t value) : _value(value) {}

		PhysicsValue (const Self &other) : _value(other.ref()) {}
		
		template <typename S>
		PhysicsValue (const PhysicsValue< value_t, dimensions_t, S > &other) :
			_value(other.ToScale< value_scale_t >().ref())
		{}

		value_t &		ref ()							{ return _value; }
		value_t const &	ref ()			const			{ return _value; }

		value_t			Get ()			const			{ return value_scale_t::Func::Get( _value ); }

		Self	operator -  ()			const			{ return Self( -_value ); }

		Self &	operator =  (const Self &right)			{ _value = right.ref();  return *this; }

		bool	IsZero ()						const	{ return UXMath::IsZero( _value ); }

		bool	operator == (const Self &right)	const	{ return ( _value == right.ref() ); }
		bool	operator != (const Self &right)	const	{ return not ( *this == right ); }
		bool	operator >  (const Self &right)	const	{ return _value >  right.ref(); }
		bool	operator >= (const Self &right)	const	{ return _value >= right.ref(); }
		bool	operator <  (const Self &right)	const	{ return _value <  right.ref(); }
		bool	operator <= (const Self &right)	const	{ return _value <= right.ref(); }

		Self &	operator += (const Self &right)			{ _value += right.ref();  return *this; }
		Self &	operator -= (const Self &right)			{ _value -= right.ref();  return *this; }

		Self	operator +  (const Self &right)	const	{ return Self( _value + right.ref() ); }
		Self	operator -  (const Self &right)	const	{ return Self( _value - right.ref() ); }

		Self &	operator *= (value_t right)				{ _value *= right;  return *this; }
		Self &	operator /= (value_t right)				{ _value /= right;  return *this; }

		Self	operator *  (value_t right)		const	{ return Self( _value * right ); }
		Self	operator /  (value_t right)		const	{ return Self( _value / right ); }

		
		template <typename D, typename S>
		typename _Add< D, S >::type  operator +  (const PhysicsValue<value_t,D,S> &right) const
		{
			typedef _Add< D, S >			add_op;
			typedef typename add_op::type	result_t;

			return result_t( add_op::Get( _value, right.ref() ) );
		}
		
		template <typename D, typename S>
		typename _Sub< D, S >::type  operator -  (const PhysicsValue<value_t,D,S> &right) const
		{
			typedef _Sub< D, S >			sub_op;
			typedef typename sub_op::type	result_t;

			return result_t( sub_op::Get( _value, right.ref() ) );
		}
		
		template <typename D, typename S>
		typename _Mul< D, S >::type  operator *  (const PhysicsValue<value_t,D,S> &right) const
		{
			typedef _Mul< D, S >			mul_op;
			typedef typename mul_op::type	result_t;

			return result_t( mul_op::Get( _value, right.ref() ) );
		}
		
		template <typename D, typename S>
		typename _Div< D, S >::type  operator /  (const PhysicsValue<value_t,D,S> &right) const
		{
			typedef _Div< D, S >			div_op;
			typedef typename div_op::type	result_t;

			return result_t( div_op::Get( _value, right.ref() ) );
		}


		friend Self	operator * (value_t left, const Self &right)
		{
			return Self( left * right.ref() );
		}
	

		friend SelfInversed	operator / (value_t left, const Self &right)
		{
			return SelfInversed( left / right.ref() );
		}

		
		template <isize PowNum, isize PowDenom>
		typename _Pow< PowNum, PowDenom >::type  Pow () const
		{
			typedef typename _Pow< PowNum, PowDenom >::type							result_t;
			typedef typename CompileTime::Fractional32< PowNum, PowDenom >::type	pow_t;
			typedef typename TypeInfo<ValueType>::Similar_Float						float_t;

			return result_t( UXMath::Pow( _value, pow_t::ToFloat< float_t >() ) );
		}
		

		template <isize Power>
		typename _Pow< Power >::type  Pow () const
		{
			return typename _Pow< Power >::type( UXMath::Pow< Power >( _value ) );
		}


		typename _Pow< 2 >::type  Square () const
		{
			return Pow< 2 >();
		}


		typename _Pow< 1, 2 >::type  Sqrt () const
		{
			return Pow< 1, 2 >();
		}


		SelfInversed  Inverse () const
		{
			return value_t(1) / (*this);
		}


		Self &	SetMax (const Self &right)
		{
			_value = Max( _value, right._value );
			return *this;
		}


		Self &	SetMin (const Self &right)
		{
			_value = Min( _value, right._value );
			return *this;
		}


		string ToString () const
		{
			return string().FormatF( Get() ) << '[' << dimensions_t::ToString("*") << ']';
		}


		string ToDebugString () const
		{
			return string().FormatF( ref() ) << " * " << value_scale_t::ToString() << " [" << dimensions_t::ToString("*") << ']';
		}


		template <typename T>
		PhysicsValue< T, dimensions_t, typename _NewConv<T>::type >  Convert () const
		{
			return PhysicsValue< T, dimensions_t, typename _NewConv<T>::type >( T( _value ) );
		}


		template <typename T>
		T	To () const
		{
			STATIC_ASSERTE( dimensions_t::Equal< typename T::dimensions_t >::value );

			typedef typename TypeDescriptor::MainType< value_t, typename T::value_t >::type		main_value_t;
			typedef typename value_scale_t::To< main_value_t >::type							scale1_t;
			typedef typename T::value_scale_t::To< main_value_t >::type							scale2_t;
			typedef typename scale1_t::Div< scale2_t >											div_op_t;

			return T( (T::value_t) div_op_t::Get( _value, main_value_t(1) ) );
		}


		template <typename ToValueScale>
		PhysicsValue< value_t, dimensions_t, ToValueScale >	ToScale () const
		{
			return PhysicsValue< value_t, dimensions_t, ToValueScale >(
						Get() / ToValueScale::Func::Get( value_t(1) ) );
		}
	};
	


	//
	// Non-Dimensional Physics Value
	//

	template <typename ValueType,
			  typename ValueScale
			 >
	struct PhysicsValue< ValueType, DefaultPhysicsDimensionsList::CreateNonDimensional::type, ValueScale >
	{
	// types
	public:
		typedef ValueType													value_t;
		typedef DefaultPhysicsDimensionsList::CreateNonDimensional::type	dimensions_t;
		typedef ValueScale													value_scale_t;
		typedef PhysicsValue< value_t, dimensions_t, value_scale_t >		Self;
		
		STATIC_ASSERTE( dimensions_t::IsNonDimensional::value );

	private:
		typedef PhysicsValue< ValueType, typename dimensions_t::Inverse::type,
							  typename value_scale_t::Inverse::type >				SelfInversed;


	// variables
	private:
		value_t	_value;


	// methods
	public:
		PhysicsValue () : _value(0) {}
	
		explicit
		PhysicsValue (value_t value) : _value(value) {}

		PhysicsValue (const Self &other) : _value(other.ref()) {}
		

		value_t &		ref ()							{ return _value; }
		value_t const &	ref ()			const			{ return _value; }

		value_t			Get ()			const			{ return value_scale_t::Func::Get( _value ); }

		operator value_t ()				const			{ return Get(); }

		Self &	operator =  (const Self &right)			{ _value = right.ref();  return *this; }

		bool	operator == (const Self &right)	const	{ return ( _value == right.ref() ); }
		bool	operator != (const Self &right)	const	{ return not ( *this == right ); }
		bool	operator >  (const Self &right)	const	{ return _value >  right.ref(); }
		bool	operator >= (const Self &right)	const	{ return _value >= right.ref(); }	// TODO: with Equals
		bool	operator <  (const Self &right)	const	{ return _value <  right.ref(); }
		bool	operator <= (const Self &right)	const	{ return _value <= right.ref(); }	// TODO: with Equals

		Self &	operator += (const Self &right)			{ _value += right.ref();  return *this; }
		Self &	operator -= (const Self &right)			{ _value -= right.ref();  return *this; }
		
		Self	operator +  (const Self &right)	const	{ return Self( _value + right.ref() ); }
		Self	operator -  (const Self &right)	const	{ return Self( _value - right.ref() ); }

		Self &	operator *= (value_t right)				{ _value *= right;  return *this; }
		Self &	operator /= (value_t right)				{ _value /= right;  return *this; }
		
		Self	operator *  (value_t right)		const	{ return Self( _value * right ); }
		Self	operator /  (value_t right)		const	{ return Self( _value / right ); }


		friend Self	operator * (value_t left, const Self &right)
		{
			return Self( left * right.ref() );
		}
	
		friend SelfInversed	operator / (value_t left, const Self &right)
		{
			return SelfInversed( left / right.ref() );
		}


		template <isize PowNum, isize PowDenom>
		value_t  Pow () const
		{
			typedef typename CompileTime::Fractional32< PowNum, PowDenom >::type	pow_t;
			typedef typename TypeInfo< value_t >::Similar_Float						float_t;

			return UXMath::Pow( Get(), pow_t::ToFloat< float_t >() );
		}
		

		template <isize Power>
		value_t  Pow () const
		{
			return UXMath::Pow< Power >( Get() );
		}


		value_t  Square () const
		{
			return UXMath::Square( Get() );
		}


		value_t  Sqrt () const
		{
			return UXMath::Sqrt( Get() );
		}


		SelfInversed  Inverse () const
		{
			return SelfInversed( value_t(1) / _value );
		}


		string ToString () const
		{
			return string().FormatF( Get() ) << " []";
		}


		string ToDebugString () const
		{
			return string().FormatF( ref() ) << " * (" << value_scale_t::Scale::ToString()
					<< ")^" << value_scale_t::Power::ToString() << " []";
		}
	};



	//
	// Default Physics Values
	//

	template <typename T>
	struct DefaultPhysicsValues
	{
		typedef DefaultPhysicsDimensions	_def;

		typedef PhysicsValue< T, _def::NonDimensional >			NonDimensional;

		// base //
		typedef PhysicsValue< T, _def::Second >					Seconds;		// s
		typedef PhysicsValue< T, _def::Kilogram >				Kilograms;		// kg
		typedef PhysicsValue< T, _def::Meter >					Meters;			// m
		typedef PhysicsValue< T, _def::Amper >					Ampers;			// A
		typedef PhysicsValue< T, _def::Kelvin >					Kelvins;		// K
		typedef PhysicsValue< T, _def::Mole >					Moles;			// mol
		typedef PhysicsValue< T, _def::Candela >				Candelas;		// cd
		typedef PhysicsValue< T, _def::Currency >				Currencies;		// $
		typedef PhysicsValue< T, _def::Bit >					Bits;			// bit


		// compound //
		typedef PhysicsValue< T, _def::SquareMeter >			SquareMeters;				// m^2
		typedef PhysicsValue< T, _def::CubicMeter >				CubicMeters;				// m^3
		typedef PhysicsValue< T, _def::MeterPerSecond >			MetersPerSeconds;			// m/s
		typedef PhysicsValue< T, _def::MeterPerSquareSecond >	MetersPerSquareSeconds;		// m/s^2
		typedef PhysicsValue< T, _def::KilogramPerSecond >		KilogramsPerSeconds;		// kg / s
		typedef PhysicsValue< T, _def::KilogramMeterPerSecond >	KilogramMetersPerSeconds;	// kg * m/s
		typedef PhysicsValue< T, _def::KilogramPerCubicMeter >	KilogramsPerCubicMeters;	// p = kg / m^3
		typedef PhysicsValue< T, _def::Newton >					Newtons;					// N
		typedef PhysicsValue< T, _def::Joule >					NewtonMeters;				// N*m
		typedef PhysicsValue< T, _def::Joule >					Joules;						// J
		typedef PhysicsValue< T, _def::Pascal >					Pascals;					// Pa
		typedef PhysicsValue< T, _def::Hertz >					Hertz;						// Hz
		typedef PhysicsValue< T, _def::Watt >					Watts;						// W
		typedef PhysicsValue< T, _def::Coulomb >				Coulombs;					// C
		typedef PhysicsValue< T, _def::Volt >					Volts;						// V
		typedef PhysicsValue< T, _def::Ohm >					Ohms;						// Ohm
		typedef PhysicsValue< T, _def::Farad >					Farads;						// F
		typedef PhysicsValue< T, _def::Weber >					Webers;						// Wb
		typedef PhysicsValue< T, _def::Henry >					Henries;					// H
		typedef PhysicsValue< T, _def::Tesla >					Teslas;						// T
		typedef PhysicsValue< T, _def::Siemens >				Siemens;					// S
		typedef PhysicsValue< T, _def::Lumen >					Lumens;						// lm
		typedef PhysicsValue< T, _def::Lux >					Lux;						// lx
		typedef PhysicsValue< T, _def::AmperPerMeter >			AmpersPerMeters;			// A/m
		typedef PhysicsValue< T, _def::KilogramPerMole >		KilogramsPerMoles;			// kg/mol


		// with scale //
		typedef PhysicsDimensionScale<T>													_scale;
		typedef typename _scale::SpeedOfLight::UnitMul< typename _scale::Minute >::type		_lightMinute;
		typedef typename _scale::SpeedOfLight::UnitMul< typename _scale::Hour >::type		_lightHour;
		typedef typename _scale::SpeedOfLight::UnitMul< typename _scale::Day >::type		_lightDay;
		typedef typename _scale::SpeedOfLight::UnitMul< typename _scale::Year >::type		_lightYear;
		typedef typename _scale::NauticalMile::UnitDiv< typename _scale::Hour >::type		_knot;
		typedef typename _scale::GravitationalAcceleration									_gravAccel;
		typedef typename _scale::GravitationalConstant										_gravConst;
		typedef typename _scale::Year::UnitMul< typename _scale::Kilo >::type				_thousandYears;
		typedef typename _scale::Year::UnitMul< typename _scale::Mega >::type				_millionYears;
		typedef typename _scale::Milli::UnitMul< typename _scale::Deci >::type				_gaussToTesla;

		typedef _def::CubicMeter::Div< _def::Kilogram::Mul< _def::Second::Power<
					CompileTime::Fractional32< 2 >::type >::type >::type >::type			_gravConstType;

		typedef _def::Newton::Div< _def::Amper::Power<
					CompileTime::Fractional32< 2 >::type >::type >::type					_newtonPerSquareAmper;

		typedef typename _scale::VacuumPermeabilityConst									_vacuumPermeability;

		typedef _def::Mole::Inverse::type													_invMole;

		typedef _def::Joule::Div< _def::Kelvin::Mul< _def::Mole >::type >::type				_jouleKelvinMole;

		typedef PhysicsValue< T, _def::Meter, typename _scale::Nano >						Nanometers;			// nm
		typedef PhysicsValue< T, _def::Meter, typename _scale::Micro >						Micrometers;		// um
		typedef PhysicsValue< T, _def::Meter, typename _scale::Milli >						Millimeters;		// mm
		typedef PhysicsValue< T, _def::Meter, typename _scale::Centi >						Centimeters;		// cm
		typedef PhysicsValue< T, _def::Meter, typename _scale::Kilo >						Kilometers;			// km
		typedef PhysicsValue< T, _def::Meter, typename _scale::Mega >						Megameters;			// Mm
		typedef PhysicsValue< T, _def::Meter, typename _scale::NauticalMile >				NauticalMiles;		// 
		typedef PhysicsValue< T, _def::Meter, typename _scale::StatuteMile >				StatuteMile;		// 
		typedef PhysicsValue< T, _def::Meter, typename _scale::Inch >						Inch;				// in
		typedef PhysicsValue< T, _def::Meter, typename _scale::Foot >						Foot;				// ft
		typedef PhysicsValue< T, _def::Meter, typename _scale::League >						League;				// 
		typedef PhysicsValue< T, _def::Meter, typename _scale::Yard >						Yard;				// 
		typedef PhysicsValue< T, _def::Meter, typename _scale::Angstrom >					Angstroms;			// A
		typedef PhysicsValue< T, _def::Meter, typename _scale::SpeedOfLight >				LightSeconds;		// c*s
		typedef PhysicsValue< T, _def::Meter, _lightMinute >								LightMinutes;		// c*min
		typedef PhysicsValue< T, _def::Meter, _lightHour >									LightHours;			// c*h
		typedef PhysicsValue< T, _def::Meter, _lightDay >									LightDays;			// c*d
		typedef PhysicsValue< T, _def::Meter, _lightYear >									LightYears;			// c*y
		typedef PhysicsValue< T, _def::Meter, typename _scale::Parsec >						Parsecs;			// pc
		typedef PhysicsValue< T, _def::Meter, typename _scale::AstronomicalUnit >			AstronomicalUnits;	// au
		typedef PhysicsValue< T, _def::Meter, typename _scale::SolarRadius >				SolarRadius;		// 
		
		typedef PhysicsValue< T, _def::SquareMeter, typename _scale::Barn >					Barns;				// b
		
		typedef PhysicsValue< T, _def::CubicMeter, typename _scale::Litre >					Liters;				// L
		typedef PhysicsValue< T, _def::CubicMeter, typename _scale::USGallon >				Gallon;				// gal
		
		typedef PhysicsValue< T, _def::MeterPerSecond, typename _scale::SpeedOfLight >		LightSpeeds;		// c
		typedef PhysicsValue< T, _def::MeterPerSecond, _knot >								Knots;				// kn
		typedef PhysicsValue< T, _def::MeterPerSecond, typename _scale::AstronomicalUnit >	AstronomicalUnitsPerSeconds;	// AU/s

		typedef PhysicsValue< T, _def::Kilogram, typename _scale::Dalton >					Daltons;			// Da, u
		typedef PhysicsValue< T, _def::Kilogram, typename _scale::Pico >					Nanograms;			// ng
		typedef PhysicsValue< T, _def::Kilogram, typename _scale::Nano >					Micrograms;			// ug
		typedef PhysicsValue< T, _def::Kilogram, typename _scale::Micro >					Milligrams;			// mg
		typedef PhysicsValue< T, _def::Kilogram, typename _scale::Milli >					Grams;				// g
		typedef PhysicsValue< T, _def::Kilogram, typename _scale::Kilo >					Tones;				// t
		typedef PhysicsValue< T, _def::Kilogram, typename _scale::Mega >					Kilotones;			// kt
		typedef PhysicsValue< T, _def::Kilogram, typename _scale::Giga >					Megatones;			// Mt
		typedef PhysicsValue< T, _def::Kilogram, typename _scale::Grain >					Grains;				// gr
		typedef PhysicsValue< T, _def::Kilogram, typename _scale::Pound >					Pound;				// lb
		typedef PhysicsValue< T, _def::Kilogram, typename _scale::EarthMass >				EarthMass;			// EM
		typedef PhysicsValue< T, _def::Kilogram, typename _scale::SolarMass >				SolarMass;			// SM

		typedef PhysicsValue< T, _def::Second, typename _scale::Nano >						Nanoseconds;		// ns
		typedef PhysicsValue< T, _def::Second, typename _scale::Micro >						Microseconds;		// us
		typedef PhysicsValue< T, _def::Second, typename _scale::Milli >						Milliseconds;		// ms
		typedef PhysicsValue< T, _def::Second, typename _scale::Minute >					Minutes;			// min
		typedef PhysicsValue< T, _def::Second, typename _scale::Hour >						Hours;				// h
		typedef PhysicsValue< T, _def::Second, typename _scale::Day >						Days;				// d
		typedef PhysicsValue< T, _def::Second, typename _scale::Year >						Years;				// y
		typedef PhysicsValue< T, _def::Second, _thousandYears >								ThousandYears;		// 1 000 * y
		typedef PhysicsValue< T, _def::Second, _millionYears >								MillionYears;		// 1 000 000 * y

		typedef PhysicsValue< T, _def::Joule, typename _scale::ElectronVolt >				ElectronVolts;		// eV
		typedef PhysicsValue< T, _def::Joule, typename _scale::Nano >						Nanojoules;			// nJ
		typedef PhysicsValue< T, _def::Joule, typename _scale::Micro >						Microjoules;		// uJ
		typedef PhysicsValue< T, _def::Joule, typename _scale::Milli >						Millijoules;		// mJ
		typedef PhysicsValue< T, _def::Joule, typename _scale::Kilo >						Kilojoules;			// kJ
		typedef PhysicsValue< T, _def::Joule, typename _scale::Mega >						Megajoules;			// MJ
		typedef PhysicsValue< T, _def::Joule, typename _scale::Giga >						Gigajoules;			// GJ
		
		typedef PhysicsValue< T, _def::Pascal, typename _scale::Bar >						Bars;				// bar
		typedef PhysicsValue< T, _def::Pascal, typename _scale::Atmosphere >				Atmospheres;		// atm
		
		typedef PhysicsValue< T, _def::Pascal, _gravAccel >									GAcceleration;		// g
		typedef PhysicsValue< T, _gravConstType, _gravConst >								GConstant;			// G
		
		typedef PhysicsValue< T, _def::Watt, typename _scale::SolarLuminosity >				SolarLuminosity;	// SL
		
		typedef PhysicsValue< T, _def::Bit, typename _scale::Kilobit >						Kilobit;			// Kbit
		typedef PhysicsValue< T, _def::Bit, typename _scale::Megabit >						Megabit;			// Mbit
		typedef PhysicsValue< T, _def::Bit, typename _scale::Gigabit >						Gigabit;			// Gbit
		typedef PhysicsValue< T, _def::Bit, typename _scale::Terabit >						Terabit;			// Tbit
		typedef PhysicsValue< T, _def::Bit, typename _scale::Petabit >						Petabit;			// Pbit
		typedef PhysicsValue< T, _def::Bit, typename _scale::Exabit  >						Exabit;				// Ebit
		typedef PhysicsValue< T, _def::Bit, typename _scale::Zettabit >						Zettabit;			// Zbit
		typedef PhysicsValue< T, _def::Bit, typename _scale::Yottabit >						Yottabit;			// Ybit

		typedef PhysicsValue< T, _def::Bit, typename _scale::Byte >							Bytes;				// b
		
		typedef PhysicsValue< T, _def::AmperPerMeter, typename _scale::Oersted >			Oersteds;			// Oe

		typedef PhysicsValue< T, _def::Tesla, _gaussToTesla >								Gauss;				// G, Gs

		typedef PhysicsValue< T, _def::KilogramPerCubicMeter, typename _scale::Milli >		GramsPerCubicCentimeters;	// gr / cm^3
		
		typedef PhysicsValue< T, _def::KilogramPerMole, typename _scale::Milli >			GramsPerMoles;			// gr/mol

		typedef PhysicsValue< T, _invMole, typename _scale::AvogadroConstant >				AvogadroConstant;		// Na
		typedef PhysicsValue< T, _newtonPerSquareAmper, _vacuumPermeability >				VacuumPermeabilityConst;// mu0
		typedef PhysicsValue< T, _jouleKelvinMole, typename _scale::GasConstant >			GasConstant;			// R
	};


}	// UXTypes
}	// UX_STL

#endif	// _UXS_PHYSICS_VALUE_H